package model;

import com.google.inject.Inject;
import exception.InvalidTSException;
import exception.MachineInfoNotCompleteException;
import exception.VMNotReadyException;
import exception.BuildNameNotSetException;
import exception.InvalidHostNameException;
import com.ibm.staf.STAFException;
import com.ibm.staf.STAFHandle;
import com.ibm.staf.STAFMarshallingContext;
import com.ibm.staf.STAFResult;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author pyin
 */
public class VMMachine implements Machine {

    private String os;
    private String name;
    private String vmHost;
    private String vmFileName;
    private String vmSnapShot;
    private String tsProjectPath;
    private String tsProjectArchive;
    private String installerPath;
    private String tsTcPath;
    TSM testScriptManager;
    ConfigManager configManager;
    STAFHandle handle;
    ServerUtil serverUtil;
    STAFHelper stafHelper;

    @Inject
    public VMMachine(TSM scriptManager, ConfigManager configManager, ServerUtil serverUtil, STAFHelper stafHelper) {
        this.testScriptManager = scriptManager;
        this.configManager = configManager;
        this.serverUtil = serverUtil;
        this.stafHelper = stafHelper;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 59 * hash + (this.name != null ? this.name.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof VMMachine) {
            return this.getName().equals(((VMMachine) obj).getName());
        } else {
            return super.equals(obj);
        }
    }

    public String getInstallerArchive() throws BuildNameNotSetException {
        return this.getInstallerPath() + "\\" + configManager.getBuildName() + ".zip";
    }

    /**
     * @return the os
     */
    public String getOs() {
        return os;
    }

    /**
     * @param os the os to set
     */
    public void setOs(String os) {
        this.os = os;
    }

    public void setTsProjectArchive(String name) {
        this.tsProjectArchive = name;
    }

    public String getTsProjectArchive() {
        return this.tsProjectArchive;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the vmHost
     */
    public String getVmHost() {
        return vmHost;
    }

    /**
     * @param vmHost the vmHost to set
     */
    public void setVmHost(String vmHost) {
        this.vmHost = vmHost;
    }

    /**
     * @return the vmFileName
     */
    public String getVmFileName() {
        return vmFileName;
    }

    /**
     * @param vmFileName the vmFileName to set
     */
    public void setVmFileName(String vmFileName) {
        this.vmFileName = vmFileName;
    }

    /**
     * @return the vmSnapShot
     */
    public String getVmSnapShot() {
        return vmSnapShot;
    }

    /**
     * @param vmSnapShot the vmSnapShot to set
     */
    public void setVmSnapShot(String vmSnapShot) {
        this.vmSnapShot = vmSnapShot;
    }

    public boolean hasCurrentBuildSnapshot() throws BuildNameNotSetException {
        String buildName = configManager.getBuildName();
        if (buildName == null) {
            throw new BuildNameNotSetException();
        } else {
            String[] snapshots = this.getSnapshots();
            for (int i = 0; i < snapshots.length; i++) {
                if (snapshots[i].equalsIgnoreCase(buildName)) {
//                    System.out.println("Found current buildName: "+buildName);
                    return true;
                }
            }
            return false;
        }
    }

    public boolean isStarted() {
        return stafHelper.isMachineAlive(this.getName());
    }

    public boolean isHostingMachineReady() {
        return stafHelper.isMachineAlive(this.getVmHost());
    }

    public boolean shutdown() {
        try {
            handle = new STAFHandle(configManager.getSnapshotsHandleName());
            STAFResult result = handle.submit2(this.vmHost, "process", "start command" +
                    " \"vmrun stop \\\"" + this.vmFileName.replace("\\", "\\\\") + "\\\"\" stderrtostdout returnstdout wait");
            handle.unRegister();
            if (result.rc == 0) {
                return true;
            }

        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    public String[] getSnapshots() {
        try {
            handle = new STAFHandle(configManager.getSnapshotsHandleName());
            STAFResult result = handle.submit2(this.vmHost, "process", "start command" +
                    " \"vmrun listSnapshots \\\"" + this.vmFileName.replace("\\", "\\\\") + "\\\"\" stderrtostdout returnstdout wait");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    HashMap hash = (HashMap) mc.getRootObject();
                    HashMap item = (HashMap) ((LinkedList) hash.get("fileList")).get(0);
                    if (!item.get("data").toString().startsWith("Error")) {
                        String returned = item.get("data").toString();
//                        System.out.println(returned);
                        return parseReturnedResult(returned);
                    }
//                    Iterator it = hash.keySet().iterator();
//                    while(it.hasNext()){
//                        String key = (String)it.next();
//                        System.out.println("["+key+"]="+hash.get(key).toString());
//                    }
                }
            } else {
                System.out.println(result.result);
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public boolean revertToSnapshot(String snapshotName) {
        return stafHelper.revertToSnapshot(this.getVmHost(), this.getVmFileName(), snapshotName);
    }

    public boolean start() {
        return stafHelper.startMachine(this.getName(), this.getVmHost(), this.getVmFileName()) &&
                this.setUpSTAFVars();
    }

    public int setupEnv(String buildName) throws BuildNameNotSetException {
        if (!this.hasCurrentBuildSnapshot()) {
            if (!revertToSnapshot("STAF")) {
                System.out.println("complete step 1");
                return 1;
            }
            if (!start()) {
                System.out.println("complete step 2");
                return 2;
            }
            if (!serverUtil.createServerSideProjectArchive()) {
                System.out.println("complete step 3");
                return 3;
            }
            if (!getProjectArchive()) {
                System.out.println("complete step 4");
                return 4;
            }
            if (!extractProjectArchive()) {
                System.out.println("complete step 5");
                return 5;
            }
            if (!copyInstallerArchive()) {
                System.out.println("complete step 6");
                return 6;
            }
            if (!extractInstallerArchive()) {
                System.out.println("complete step 6");
                return 7;
            }
            if (!stop()) {
                System.out.println("complete step 8");
                return 8;
            }
            if (!takeSnapshot(buildName)) {
                System.out.println("complete step 9");
                return 9;
            }
            if (!start()) {
                System.out.println("complete step 10");
                return 10;
            } else {
                return 0;
            }
        } else {
            return 0;
        }
    }


    //should not call "vmrun stop" to stop vmmachine.
    public boolean stop() {
        return stafHelper.stopMachine(this.getName());
    }

    public boolean takeSnapshot(String name) throws BuildNameNotSetException {
        if (name == null) {
            throw new BuildNameNotSetException();
        }
        return stafHelper.takeSnapshot(this.getVmHost(), this.getVmFileName(), configManager.getBuildName());
    }

    public void setInstallerPath(String path) {
        this.installerPath = path;
    }

    public String getTsTcPath() {
        return this.tsTcPath;
    }

    public void setTsTcPath(String path) {
        this.tsTcPath = path;
    }

    public boolean isInfoComplete() {
        if(this.getName()==null || this.getName().trim().equals(""))
            return false;
        if(this.getTsProjectPath()==null || this.getTsProjectPath().trim().equals(""))
            return false;
        if(this.getTsTcPath()==null || this.getTsTcPath().trim().equals(""))
            return false;
        return true;
    }

    private boolean copyInstallerArchive() throws BuildNameNotSetException {
        return stafHelper.copyDirectory("local", configManager.getCurrentInstallerArchive(), this.getName(), this.getInstallerPath());
    }

    private boolean extractInstallerArchive() throws BuildNameNotSetException {
//        try {
//            handle = new STAFHandle("");
//            STAFResult result = handle.submit2(this.name, "zip", "unzip zipfile \"" + getInstallerArchive().replace("\\", "\\\\") +
//                    "\" todirectory \"" + this.getInstallerPath().replace("\\", "\\\\") + "\" replace");
////            handle.unRegister();
//            if (result.rc == 0) {
//                result = handle.submit2(this.name, "fs", "delete entry \"" + getInstallerArchive().replace("\\", "\\\\") + "\" confirm");
//                if (result.rc == 0) {
//                    return true;
//                }
//            } else {
//                System.out.println(result.result);
//            }
//        } catch (STAFException e) {
//        }
//        return false;
        return stafHelper.unzip(this.getName(), this.getInstallerArchive(), this.getInstallerPath()) &&
                stafHelper.deleteFile(this.name, this.getInstallerArchive());
    }

    public boolean createServerSideProjectArchive() {
        return stafHelper.zip("local", configManager.getProjectArchiveName(), configManager.getProjectPath(), configManager.getProjectPath());
//        try {
//            File archive = new File(configManager.getProjectArchiveName());
//            if (archive.exists()) {
//                archive.delete();
//            }
//            handle = new STAFHandle(configManager.getZipHandleName());
//            STAFResult result = handle.submit2(configManager.getServerHostName(), "zip", "add zipfile " + "\"" + configManager.getProjectArchiveName().replace("\\", "/") +
//                    "\" directory \"" + configManager.getProjectPath().replace("\\", "/") + "\" relativeto \"" + configManager.getProjectPath().replace("\\", "/") + "\" recurse");
//            handle.unRegister();
//            if (result.rc == 0) {
//                return true;
//            } else {
//                System.out.println(result.result);
//            }
//        } catch (STAFException ex) {
//            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
//        }
//        return false;

    }

    //Copy project archive from server to remote test vm machine: local
    public boolean getProjectArchive() {
        return stafHelper.copyFile("local", configManager.getProjectArchiveName(), this.getName(), this.getTsProjectArchive());
    }

    public boolean extractProjectArchive() {
        return stafHelper.unzip(this.getName(), this.getTsProjectArchive(), this.getTsProjectPath());
    }

    public int restore(String snapshotName) {
        if (!revertToSnapshot(snapshotName)) {
            return 1;
        }
        if (!start()) {
            return 2;
        }
        if (!serverUtil.createServerSideProjectArchive()) {
            return 3;
        }
        if (!getProjectArchive()) {
            return 4;
        }
        if (!extractProjectArchive()) {
            return 5;
        } else {
            return 0;
        }
//        return (revertToSnapshot(snapshotName) && start() && createServerSideProjectArchive() && getProjectArchive() && extractProjectArchive());
    }

//    public static void main(String[] args) {
//        try {
////        try {
////            VMMachine machine = VMMachineFactory.getVMMachine("win03uepinstall");
////            System.out.println(machine.setupEnv("uep10drop_milestone22"));
//            VMMachine machine = VMMachineFactory.getVMMachine("win03uepinstall");
//            ConfigManager.setBuildName("uep10drop_milestone22");
//            System.out.println(machine.stop());
////            machine.setTsProjectArchive("e:\\test.zip");
////            machine.copyInstaller();
////            System.out.println("getProjectArchive:"+machine.getProjectArchive());
////            System.out.println("extractProjectArchive:"+machine.extractProjectArchive());
//
//
////            machine.restore(ConfigManager.getBuildName());
////            System.out.println(machine.setupEnv("m3b"));
////            System.out.println(machine.getOs());
////        } catch (BuildNameNotSetException ex) {
////            System.out.println(ex.getMessage());
////        } catch (InvalidHostNameException e){
////            System.out.println(e.getMessage());
////        }
//        } catch (Exception ex) {
//            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
//        }
////            System.out.println(machine.getOs());
//
//
//
////        } catch (BuildNameNotSetException ex) {
////            System.out.println(ex.getMessage());
////        } catch (InvalidHostNameException e){
////            System.out.println(e.getMessage());
////        }
//
//    }
    public void setTsProjectPath(String path) {
        this.tsProjectPath = path;
    }

    public String getTsProjectPath() {
        return this.tsProjectPath;
    }

    private String[] parseReturnedResult(String returned) {
        String[] result = returned.split("\r\n");
        String[] x = new String[result.length - 1];
        for (int i = 0; i < x.length; i++) {
            x[i] = result[i + 1];
        }
        return x;
    }

    public TestScript getRunningScript() throws VMNotReadyException, InvalidTSException, InvalidHostNameException, BuildNameNotSetException, MachineInfoNotCompleteException {
        try {
            handle = new STAFHandle(configManager.getListHandlesHandleName());
            STAFResult result = handle.submit2(this.vmHost, "handle", "list handles");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    LinkedList list = (LinkedList) mc.getRootObject();
                    Iterator it = list.iterator();
                    while (it.hasNext()) {
                        HashMap entry = (HashMap) it.next();
//                        System.out.println(entry.get("name"));
                        String name = entry.get("name").toString();
                        if (name.startsWith("HN_")) {
                            return testScriptManager.getScript(name.replace("HN_", "").trim());
                        }
                    }
                }
            }
            if (result.rc == 16) {
                throw new InvalidTSException();
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public boolean setUpSTAFVars() {
        return stafHelper.setupSTAFVars(this.getName(), "INTMONITOR/INSTALLER_PATH", this.getInstallerPath());
    }

    /**
     * @return the installerPath
     */
    public String getInstallerPath() {
        return installerPath;
    }
}
